package com.design.cfr;

import java.util.ArrayList;
import java.util.List;

/**
 * Chain responsibility abstract  责任链抽象
 * T --> apply(T t)的参数
 * AR --> apply(T t)的返回值
 * SR --> start()返回值
 * @description:
 * @author: cyj
 * @date 2022 -08-12 15:37:13
 */
public abstract class ChainResponsibility<T, AR, SR> implements ChainResponsibilityElement<T, AR, ChainResponsibility> {

    /**
     * Next 下一个元素
     */
    private ChainResponsibility next;

    @Override
    public ChainResponsibility getNext() {
        return next;
    }

    @Override
    public void setNext(ChainResponsibility chainResponsibility) {
        this.next = chainResponsibility;
    }

    /**
     * To list list 责任链转list
     *
     * @return the list
     */
    public List<ChainResponsibility<T, AR, SR>> toList() {
        List<ChainResponsibility<T, AR, SR>> chainResponsibilities = new ArrayList<>();
        ChainResponsibility<T, AR, SR> element = this;
        // 获取下意个元素
        do {
            chainResponsibilities.add(element);
            element= element.getNext();
        } while (null != element);
        return chainResponsibilities;
    }

    /**
     * Start 启动责任链, 异常会终止
     *
     * @param t t
     * @return the sr
     */
    public abstract SR start(T t);

    /**
     * Start 启动责任链， 异常不会终止
     *
     * @param t t
     * @return the sr
     */
    public abstract SR startNothrow(T t);

    /**
     * Builder
     *
     * @description: 责任链构造者
     * @author: cyj
     * @date 2022 -08-12 15:37:13
     */
    public static class Builder<T, AR, SR> {

        /**
         * Header
         */
        private ChainResponsibility<T, AR, SR> header = null;

        /**
         * Tail
         */
        private ChainResponsibility<T, AR, SR> tail = null;

        /**
         * Add builder 添加元素
         * 需要确保
         * * T --> apply(T t)的参数
         *  * AR --> apply(T t)的返回值
         *  * SR --> start()返回值
         *  全链一致， 不然执行会报错转型，因为参数是头节点传入，这三个枚举都是以头节点为准
         * @param element element
         * @return the builder
         */
        public Builder add(ChainResponsibility<T, AR, SR> element) {
            if (this.header == null) {
                this.header = this.tail = element;
            } else if (this.tail == null) {
                this.tail = element;
                this.header.setNext(element);
            } else {
                this.tail.setNext(element);
                this.tail = element;
            }
            return this;
        }

        /**
         * Build chain responsibility element
         *
         * @return the chain responsibility element
         */
        public ChainResponsibility<T, AR, SR> build() {
            return this.header;
        }
    }
}
